#set( $symbol_pound = '#' )
#set( $symbol_dollar = '$' )
#set( $symbol_escape = '\' )
package ${package}.stream;

import java.util.Collection;
import java.util.function.BiConsumer;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.function.Supplier;

public final class Reducers {

    private Reducers() {}

    public static class ReduceImpl<T, A, R> implements Reduce<T, A, R> {
        private final Supplier<A> supplier;
        private final BiConsumer<A, T> accumulator;
        private final BinaryOperator<A> combiner;
        private final Function<A, R> finisher;

        public ReduceImpl(Supplier<A> supplier, BiConsumer<A, T> accumulator, BinaryOperator<A> combiner, Function<A, R> finisher) {
            this.supplier = supplier;
            this.accumulator = accumulator;
            this.combiner = combiner;
            this.finisher = finisher;
        }

        public static <T, A, R> ReduceImpl<T, A, R> of(Supplier<A> supplier, BiConsumer<A, T> accumulator, BinaryOperator<A> combiner) {
            return new ReduceImpl<>(supplier, accumulator, combiner, a -> (R) a);
        }
        @Override
        public Supplier supplier() {
            return supplier;
        }

        @Override
        public BiConsumer accumulator() {
            return accumulator;
        }

        @Override
        public BinaryOperator combiner() {
            return combiner;
        }

        @Override
        public Function finisher() {
            return finisher;
        }
    }

    public static <T,A extends Collection<T>> Reduce<T, A, A> toCollection(Supplier<A> supplier){
        return ReduceImpl.of(supplier, Collection ::add, (a1, a2) -> {
            a1.addAll(a2);
            return a1;
        });
    }
}
